Add MiniMax LLM adapter#2156
Conversation
|
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (5)
Summary by CodeRabbit
WalkthroughThis PR adds a new MiniMax branded OpenAI-compatible LLM adapter to the Unstract SDK, including a ChangesMiniMax LLM Adapter
Estimated code review effort: 2 (Simple) | ~15 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
for more information, see https://pre-commit.ci
|
|
| Filename | Overview |
|---|---|
| unstract/sdk1/src/unstract/sdk1/adapters/llm1/minimax.py | New MiniMaxLLMAdapter class that faithfully mirrors the NvidiaBuild pattern; all static methods present, correct provider string and icon path. |
| unstract/sdk1/src/unstract/sdk1/adapters/base1.py | Adds MiniMaxLLMParameters with the correct API base and delegates validation to _validate_branded_openai_compatible; consistent with NvidiaBuildLLMParameters. |
| unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json | Schema exposes an enable_reasoning toggle that sends reasoning_effort via extra_body, but MiniMax's API uses a thinking field for reasoning — the toggle will silently have no effect while stripping temperature. |
| unstract/sdk1/src/unstract/sdk1/adapters/llm1/init.py | Registers MiniMaxLLMAdapter in both the import list and all; alphabetical ordering maintained. |
| unstract/sdk1/tests/test_branded_openai_adapters.py | Good parametrized coverage of registration, default API base, and base-override; no test covers enable_reasoning for MiniMax where the mismatch would surface. |
| frontend/public/icons/adapter-icons/MiniMax.png | New icon file added; path matches the string returned by MiniMaxLLMAdapter.get_icon(). |
Sequence Diagram
%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
participant UI as Frontend UI
participant Adapter as MiniMaxLLMAdapter
participant Params as MiniMaxLLMParameters
participant Base as _validate_branded_openai_compatible
participant OAICompat as OpenAICompatibleLLMParameters.validate
participant LiteLLM as LiteLLM (custom_openai/)
participant API as api.minimax.io/v1
UI->>Adapter: get_json_schema()
Adapter-->>UI: minimax.json (model, api_key, api_base, enable_reasoning...)
UI->>Params: validate(adapter_metadata)
Params->>Base: _validate_branded_openai_compatible(metadata, _MINIMAX_API_BASE)
Base->>Base: fill api_base if blank
Base->>OAICompat: validate(metadata)
OAICompat->>OAICompat: prepend custom_openai/ to model
OAICompat->>OAICompat: "handle enable_reasoning → extra_body{reasoning_effort}"
OAICompat-->>Base: validated dict
Base-->>Params: validated dict
Params-->>UI: "{model: custom_openai/MiniMax-M3, api_base: https://api.minimax.io/v1, ...}"
UI->>LiteLLM: "completion(model=custom_openai/MiniMax-M3, api_base=...)"
LiteLLM->>API: POST /v1/chat/completions
API-->>LiteLLM: response
LiteLLM-->>UI: LLM response
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
participant UI as Frontend UI
participant Adapter as MiniMaxLLMAdapter
participant Params as MiniMaxLLMParameters
participant Base as _validate_branded_openai_compatible
participant OAICompat as OpenAICompatibleLLMParameters.validate
participant LiteLLM as LiteLLM (custom_openai/)
participant API as api.minimax.io/v1
UI->>Adapter: get_json_schema()
Adapter-->>UI: minimax.json (model, api_key, api_base, enable_reasoning...)
UI->>Params: validate(adapter_metadata)
Params->>Base: _validate_branded_openai_compatible(metadata, _MINIMAX_API_BASE)
Base->>Base: fill api_base if blank
Base->>OAICompat: validate(metadata)
OAICompat->>OAICompat: prepend custom_openai/ to model
OAICompat->>OAICompat: "handle enable_reasoning → extra_body{reasoning_effort}"
OAICompat-->>Base: validated dict
Base-->>Params: validated dict
Params-->>UI: "{model: custom_openai/MiniMax-M3, api_base: https://api.minimax.io/v1, ...}"
UI->>LiteLLM: "completion(model=custom_openai/MiniMax-M3, api_base=...)"
LiteLLM->>API: POST /v1/chat/completions
API-->>LiteLLM: response
LiteLLM-->>UI: LLM response
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.
---
### Issue 1 of 1
unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json:59-64
**`enable_reasoning` toggle silently has no effect on MiniMax**
The adapter routes reasoning through `OpenAICompatibleLLMParameters.validate()`, which sends `reasoning_effort` in `extra_body`. MiniMax's OpenAI-compatible API does not recognise `reasoning_effort`; it exposes deep thinking via a `thinking: {type: "adaptive"}` field instead ([MiniMax docs](https://platform.minimax.io/docs/guides/text-generation)). A user who turns this toggle on will have their `temperature` stripped from the request (potentially changing generation behaviour) while MiniMax silently ignores the unknown `reasoning_effort` key — no actual reasoning is activated.
Reviews (1): Last reviewed commit: "[pre-commit.ci] auto fixes from pre-comm..." | Re-trigger Greptile
| "enable_reasoning": { | ||
| "type": "boolean", | ||
| "title": "Enable Reasoning", | ||
| "default": false, | ||
| "description": "Toggle on for reasoning models to route reasoning_effort via the OpenAI-compatible request body." | ||
| } |
There was a problem hiding this comment.
enable_reasoning toggle silently has no effect on MiniMax
The adapter routes reasoning through OpenAICompatibleLLMParameters.validate(), which sends reasoning_effort in extra_body. MiniMax's OpenAI-compatible API does not recognise reasoning_effort; it exposes deep thinking via a thinking: {type: "adaptive"} field instead (MiniMax docs). A user who turns this toggle on will have their temperature stripped from the request (potentially changing generation behaviour) while MiniMax silently ignores the unknown reasoning_effort key — no actual reasoning is activated. Is there a plan to map enable_reasoning → thinking: {type: "adaptive"} in MiniMax's request body, or should the enable_reasoning section be removed from minimax.json until that mapping is implemented?
Prompt To Fix With AI
This is a comment left during a code review.
Path: unstract/sdk1/src/unstract/sdk1/adapters/llm1/static/minimax.json
Line: 59-64
Comment:
**`enable_reasoning` toggle silently has no effect on MiniMax**
The adapter routes reasoning through `OpenAICompatibleLLMParameters.validate()`, which sends `reasoning_effort` in `extra_body`. MiniMax's OpenAI-compatible API does not recognise `reasoning_effort`; it exposes deep thinking via a `thinking: {type: "adaptive"}` field instead ([MiniMax docs](https://platform.minimax.io/docs/guides/text-generation)). A user who turns this toggle on will have their `temperature` stripped from the request (potentially changing generation behaviour) while MiniMax silently ignores the unknown `reasoning_effort` key — no actual reasoning is activated. Is there a plan to map `enable_reasoning` → `thinking: {type: "adaptive"}` in MiniMax's request body, or should the `enable_reasoning` section be removed from `minimax.json` until that mapping is implemented?
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Confirmed — this is a real issue, not just a doc mismatch.
The reasoning path in OpenAICompatibleLLMParameters.validate() (base1.py) does two things when enable_reasoning is on: it puts reasoning_effort into extra_body and pops temperature (validated.pop("temperature", None)). MiniMax's OpenAI-compatible endpoint doesn't recognise reasoning_effort — it gates deep thinking via thinking: {type: "adaptive"} — so the key is silently ignored while the user's temperature is stripped. The toggle activates no reasoning but changes generation behaviour.
Severity is bounded to the opt-in case: it only fires if a user flips the toggle. It won't auto-enable, since _is_openai_reasoning_model matches only o1|o3|o4|gpt-5, not MiniMax-M3.
Fix: dropping the enable_reasoning block (and its allOf conditional) from minimax.json for now, since the shared OpenAI-compatible validate path has no way to emit MiniMax's thinking field. A proper enable_reasoning → thinking:{type:"adaptive"} mapping would be a follow-up in the validate layer.
| ) | ||
|
|
||
|
|
||
| class MiniMaxLLMParameters(OpenAICompatibleLLMParameters): |
There was a problem hiding this comment.
Routing through custom_openai/ silently zeroes out cost tracking for MiniMax.
_validate_branded_openai_compatible delegates to OpenAICompatibleLLMParameters.validate(), whose validate_model() unconditionally prepends custom_openai/. cost_model is only populated when the endpoint is OpenAI's own (base1.py:483), so it stays unset here, and audit.py:98 ends up calling cost_per_token(model="custom_openai/MiniMax-M3") — which raises, gets swallowed by the bare except, and records cost_in_dollars = 0.0 at debug level.
LiteLLM already ships a native MiniMax provider, and it prices M3:
litellm.get_llm_provider("minimax/MiniMax-M3") -> ('MiniMax-M3', 'minimax', None, None)
cost_per_token("custom_openai/MiniMax-M3") -> raises => cost = 0.0
cost_per_token("minimax/MiniMax-M3") -> $0.30 / $1.20 per 1M tokens
So every MiniMax token would bill as $0 in usage tracking, silently.
This is why OpenRouterLLMParameters extends BaseChatCompletionParameters instead of OpenAICompatibleLLMParameters — its own docstring says so: "Routed through LiteLLM's native openrouter/ provider so per-token costs resolve and reasoning params map without provider-specific workarounds." MiniMax should follow OpenRouter's template, not NVIDIA Build's.
Worth noting NVIDIA Build's custom_openai/ routing is correct for NVIDIA — LiteLLM prices zero nvidia_nim/ chat models (3 entries, all rerankers, all $0.0), so there's nothing to forfeit there. That reason doesn't carry over to MiniMax.
Bonus: MinimaxChatConfig.get_api_base() already returns https://api.minimax.io/v1, so going native lets you drop the hardcoded _MINIMAX_API_BASE constant entirely.
|
|
||
| @staticmethod | ||
| def get_icon() -> str: | ||
| return "/icons/adapter-icons/MiniMax.png" |
There was a problem hiding this comment.
The shipped MiniMax.png is the OpenRouter logo.
frontend/public/icons/adapter-icons/MiniMax.png is byte-for-byte identical to OpenRouter.png:
0980b0d3102c84da9d332862c563c44f MiniMax.png
0980b0d3102c84da9d332862c563c44f OpenRouter.png
676be9d2fc5cec78d5be80af03eb9fd4 NvidiaBuild.png
Both 29953 bytes. Rendering it confirms it's the OpenRouter fork-arrow mark. NvidiaBuild.png differs, so this isn't a shared-placeholder convention — it looks like the icon was copied along with the adapter template and never swapped.
Users would see OpenRouter's branding on the MiniMax card in the adapter picker. Needs the real MiniMax logo.



Reason: 仿 nvidia_build/openrouter 品牌化 OpenAI 兼容 adapter:在 adapters/llm1 加 MiniMax.py + static/MiniMax.json,base1.py 加 MiniMaxLLMParameters 固定默认 api_base,__...
Summary
Checks
git add -A -N && git diff HEAD | grep -E "$SECRET_RE"(passed)uv --directory unstract/sdk1 run ruff check src/unstract/sdk1/adapters/base1.py src/unstract/sdk1/adapters/llm1/minimax.py src/unstract/sdk1/adapters/llm1/__init__.py tests/test_branded_openai_adapters.pyuv --directory unstract/sdk1 run pytest tests/test_branded_openai_adapters.py -k 'not embedding_strips_embed_batch_size_before_litellm and not nvidia_embedding_batch_sends_passage_input_type and not compatible_embedding_omits_input_type'Note: Full
tests/test_branded_openai_adapters.pywas attempted but three existing embedding tests could not importmagicbecause system libmagic is not installed in this environment.